import java.io.*;
import java.util.*;

import org.antlr.runtime.*;             // ANTLR runtime library
import org.antlr.runtime.tree.*;        // For ANTLR's Tree classes
import org.antlr.stringtemplate.*;      // For the DOTTreeGenerator

/** 
 * Program that creates and starts the lexer, parser, etc.
 * @author  Theo Ruys, edited by Keith Davelaar
 * @version 2012.06.04
 */
public class Compiler {
	
	/** Option: Print the AST as a String */
	private static boolean  opt_ast             = false;
	/** Option: Print the AST as a DOT specification */
	private static boolean  opt_dot             = false;
    /** Option: Don't check the code, just parse and lex it */
	private static boolean 	opt_no_checker      = false;
    /** Option: Generate code for the program */
	private static boolean	opt_no_code_generator 	= false;
    
    /**
     * Check whether the options that are given are valid and process this, 
     * so it can be used later on in the compiler
     * @param args The options that are passed to the compiler
     */
    public static void parseOptions(String[] args) {
		for (int i=0; i<args.length; i++) {
            if (args[i].equals("-ast"))
                opt_ast = true;
            else if (args[i].equals("-dot"))
                opt_dot = true;
            else if (args[i].equals("-no_checker"))
                opt_no_checker = true;
            else if (args[i].equals("-no_code_generator"))
            	opt_no_code_generator = true;
        	else {
                System.err.println("error: unknown option '" + args[i] + "'");
                System.err.println("valid options: -ast -dot " +
                                   "-no_checker -no_code_generator");
                System.exit(1);
            }
        }
    }
    
    /**
     * Perform Parsing, Lexing, Checking and/or Code generation (Depending on the options
     * @param args The options that are passed to the compiler
     */
    public static void perform(String[] args){
    	try {
//    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//    		
//    		System.out.println("Please enter option(s) separated by spaces, or press enter if none desired.");
//    		System.out.println("Valid options: -ast -dot -no_checker -no_code_generator");
//    		System.out.println("-ast\t\tPrint out a textual representation of the AST");
//    		System.out.println("-dot\t\tPrint out a DOT specification for the AST");
//    		System.out.println("-no_checker\t\tSkip checking and go right to code generation");
//    		System.out.println("-no_code_generator\t\tSkip code generation");
    		
//    		String[] input = br.readLine().split(" ");
//    		parseOptions(input);
    		
    		String phase = "parsing/lexing";
//    		System.out.println("Please enter the name of the file: ");
    		opt_dot = true;
//    		CompilerLexer lexer = new CompilerLexer(new ANTLRInputStream(new FileInputStream(br.readLine())));
    		CompilerLexer lexer = new CompilerLexer(new ANTLRInputStream(new FileInputStream("Test4_1.txt")));
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            CompilerParser parser = new CompilerParser(tokens);
            
			CompilerParser.program_return parse_result = parser.program();
			CommonTree tree = (CommonTree) parse_result.getTree();
			
			handleExceptions(parser.getExceptions(), phase);
			
            if (! opt_no_checker) {      // check the AST
                phase = "checking";
            	BufferedTreeNodeStream nodes = new BufferedTreeNodeStream(tree); 
                CodeChecker checker = new CodeChecker(nodes);
                CodeChecker.program_return check_result = checker.program();
                tree = (CommonTree) check_result.getTree();
                handleExceptions(checker.exceptions, phase);
            }
                        
            if (! opt_no_code_generator) {
                phase = "code generation";
            	// generate TAM assembler code
                CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
                CodeGenerator codegenerator = new CodeGenerator(nodes);
                codegenerator.program();
                
                handleExceptions(codegenerator.getExceptions(), phase);
                
            }
            
            if (opt_ast) {          // print the AST as string
                System.out.println(tree.toStringTree());
            } else if (opt_dot) {   // print the AST as DOT specification
                DOTTreeGenerator gen = new DOTTreeGenerator(); 
                StringTemplate st = gen.toDOT(tree); 
                System.out.println(st);
            }
        } catch (RecognitionException e) {
        	System.err.print(e.getClass().getCanonicalName()+" thrown because of:");
        	System.err.println(e.getMessage());
        	e.printStackTrace();
        } catch (FileNotFoundException e) {
			System.err.println("File cannot be found. Please enter a valid filename.");
			System.exit(0);
		} catch (IOException e) {
			System.err.println("Something is wrong with the IO. The comiler will output more details and exit.");
			e.printStackTrace();
		}
    }
 
    /**
     * Print error(s) and exit the program, if exceptions have occurred
     * If they haven't, do nothing
     * @param errors A list of errors that occurred during a phase
     * @param phase Phase the error(s) occurred, either during parsing/lexing, checking or code generation
     */
	private static void handleExceptions(List<RecognitionException> errors, String phase) {
		if(errors.size() != 0){
			System.err.println("Errors ocurred while "+phase+", the compiler will output all errors and exit");
			for(RecognitionException re: errors){
            	System.err.println(re.getMessage());
            }
			System.exit(0);
		}		
	}

	/**
     * The main method, which starts a compilation of a text
     * @param args The options that are passed to the compiler
     */
    public static void main(String[] args) {
        perform(args);
    }
}
